//    Copyright (C) Kherty.  All rights reserved.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.Contracts;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Threading;
using OpenLS.Spreadsheet.UI.Internals;

namespace OpenLS.Spreadsheet.UI
{
    static class GridComments
    {
        internal static void EnableCommands(UIElement grid)
        {
            grid.CommandBindings.Add(new CommandBinding(SpreadsheetCommands.NewComment, OnNewComment, OnHasNoComment));
            grid.CommandBindings.Add(new CommandBinding(SpreadsheetCommands.EditComment, OnEditComment, OnHasComment));
            grid.CommandBindings.Add(new CommandBinding(SpreadsheetCommands.DeleteComment, OnDeleteComment, OnHasComment));
            grid.CommandBindings.Add(new CommandBinding(SpreadsheetCommands.PreviousComment, OnPreviousComment, OnAlways));
            grid.CommandBindings.Add(new CommandBinding(SpreadsheetCommands.NextComment, OnNextComment, OnAlways));
            grid.CommandBindings.Add(new CommandBinding(SpreadsheetCommands.ToggleCommentVisibility, OnToggleCommentVisibility, OnAlways));
            grid.CommandBindings.Add(new CommandBinding(SpreadsheetCommands.ToggleAllCommentsVisibility, OnToggleAllCommentVisibility, OnAlways));
            grid.MouseMove += GridMouseMove;
        }

        static void GridMouseMove(object sender, MouseEventArgs e)
        {
            var g = (WorksheetGrid)sender;
            IWorksheetFragment pane = SpreadsheetElement.GetWorksheetPane((DependencyObject) sender);
            if (pane == null)
                return;
            CellAddress address = g.GetCellAddressFrom(e.GetPosition);
            Cell cell = pane.Worksheet.NonVolatileCells.GetItemIfAny(address);
            FrameworkElement parent = (FrameworkElement) g.Parent;
            EditionElement.SetHoverCell(parent, cell);
        }

        private static void OnToggleAllCommentVisibility(object sender, ExecutedRoutedEventArgs args)
        {
            Worksheet worksheet = GetWorksheet(sender);
            if (worksheet == null || worksheet.Selection == null)
                return;
            bool wasVisible = false;
            if (args.Parameter is bool)
            {
                wasVisible = !(bool)args.Parameter;
            }
            else
            {
                foreach (Cell cell in worksheet.GetCells())
                {
                    Comment comment = cell.Comment;
                    if (comment != null)
                    {
                        wasVisible = comment.IsVisible;
                        break;
                    }
                }
            }
            foreach (Cell cell in worksheet.GetCells())
            {
                Comment comment = cell.Comment;
                if (comment != null)
                    comment.IsVisible = !wasVisible;
            }
            args.Handled = true;
        }

        private static void OnToggleCommentVisibility(object sender, ExecutedRoutedEventArgs args)
        {
            Worksheet worksheet = GetWorksheet(sender);
            bool? isVisible = null;
            foreach (Cell cell in worksheet.Selection.GetNonEmptyCells())
                if (cell.Comment != null)
                {
                    isVisible = !cell.Comment.IsVisible;
                    break;
                }
            if (isVisible != null)
                foreach (Cell cell in worksheet.Selection.GetNonEmptyCells())
                    if (cell.Comment != null)
                        cell.Comment.IsVisible = isVisible.Value;
            args.Handled = true;
        }

        static void OnAlways(object sender, CanExecuteRoutedEventArgs args)
        {
            args.CanExecute = true;
        }
        static void OnNextComment(object sender, ExecutedRoutedEventArgs args)
        {
            var cells = GetCommentCells(sender);
             var ws = GetWorksheet(sender);
            var cell = ws.ActiveCell;
            int i = cells.BinarySearch(cell, new MyCellComparer());
            ws.ActiveCell = i == cells.Count - 1 ? cells[0] : cells[i + 1];

            args.Handled = true;
        }

        private class MyCellComparer
        : IComparer<Cell>
        {
            public int Compare(Cell x, Cell y)
            {
                return OnComparison(x, y);
            }
        }

        private static List<Cell>GetCommentCells(object sender)
        {
            Worksheet ws = GetWorksheet(sender);
            Cell cell = ws.ActiveCell;
            var cells = Enumerable.ToList(Enumerable.Where(ws.GetCells(), c => c.Comment != null));
            if (cell.Comment == null)
                cells.Add(cell);
            cells.Sort(new Comparison<Cell>(OnComparison));
            return cells;
        }

        private static int OnComparison(Cell cell1, Cell cell2)
        {
            if (cell1 == cell2)
                return 0;
            int i = cell1.Row.Index.CompareTo(cell2.Row.Index);
            if (i != 0)
                return i;
            return cell1.Column.Index.CompareTo(cell2.Column.Index);
            
        }

        static void OnPreviousComment(object sender, ExecutedRoutedEventArgs args)
        {
            var cells = GetCommentCells(sender);
            Worksheet ws = GetWorksheet(sender);
            Cell cell = ws.ActiveCell;
            int i = cells.BinarySearch(cell, new MyCellComparer());
            ws.ActiveCell = i == 0 ? cells[cells.Count - 1] : cells[i - 1];

            args.Handled = true;
        }

        static void OnHasComment(object sender, CanExecuteRoutedEventArgs args)
        {
            Worksheet Worksheet = GetWorksheet(sender);
            if (Worksheet == null || Worksheet.Selection == null)
                return;
            foreach (Cell cell in Worksheet.Selection.GetNonEmptyCells())
                if (cell.Comment != null)
                    args.CanExecute = true;
        }
        static void OnHasNoComment(object sender, CanExecuteRoutedEventArgs args)
        {
            Worksheet Worksheet = GetWorksheet(sender);
            if (Worksheet == null || Worksheet.Selection == null)
                return;
            args.CanExecute = false;
            int count = 0;
            foreach (Cell cell in Worksheet.Selection.GetNonEmptyCells())
            {
                if (cell.Comment == null)
                {
                    args.CanExecute = true;
                    return;
                }
                count++;
            }
            args.CanExecute = count < Worksheet.Selection.Surface;
        }
        static Worksheet GetWorksheet(object sender)
        {
            return WorksheetHelper.GetWorksheet(sender);
        }
        static void OnNewComment(object sender, ExecutedRoutedEventArgs args)
        {
            if (sender != args.Source)
                return;
            Worksheet worksheet = GetWorksheet(sender);
            if (worksheet.ActiveCell == null)
                return;
            worksheet.ActiveCell.Comment = new Comment
            {
                Text = new RichTextParagraph(),
                Author = UserManager.UserName,
            };
            var timer = new DispatcherTimer(DispatcherPriority.Background) {Interval = new TimeSpan(0, 0, 1)};
            timer.Tick += delegate
                              {
                                  EditComment(sender, worksheet.ActiveCell.Comment);
                                  timer.Stop();
                              };
            timer.Start();
            args.Handled = true;
        }

        private static void EditComment(object sender, Comment comment)
        {
            if (comment == null)
                return;
            var grid = (WorksheetGrid)sender;
            bool gridNotNull = (grid != null);
            if (!gridNotNull) return;
            // Contract.Assert(grid != null);
            var cp = (ContentPresenter)grid.TemplatedParent;
            var cpNotNull = (cp != null);
            if (!cpNotNull) return;
            // Contract.Assert(cp != null);

            DataTemplate template = cp.ContentTemplate;
            bool templateNotNull = (template != null);
            if (!templateNotNull) return;
            // Contract.Assert(template != null);
            
            var comments = (ItemsControl)template.FindName(ElementNames.CommentLayer, cp);
            bool commentNotNull = (comments != null);
            if (!commentNotNull) return;
            // Contract.Assert(comments != null);

            var contentPresenter = (ContentPresenter)comments.ItemContainerGenerator.ContainerFromItem(comment);
            bool contentPresenterNotNull = (contentPresenter != null);
            if (!contentPresenterNotNull) return;
            // Contract.Assert(contentPresenter != null);

            var selector = new CommentTemplateSelector();
            var dt = selector.SelectTemplate(contentPresenter.DataContext, contentPresenter);// (DataTemplate)contentPresenter.FindResource(new DataTemplateKey(typeof(Comment)));
            bool dtNotNull = (dt != null);
            if (!dtNotNull) return;
            
            // Contract.Assert(dt != null);
            var element = (UIElement)dt.FindName("commentRoot", contentPresenter);
            bool elementNotNull = (element != null);
            if (!elementNotNull) return;
            
            // Contract.Assert(element != null);
            CommentElement.SetIsEdited(element, true);
            element.Focus();
        }
        static void OnEditComment(object sender, ExecutedRoutedEventArgs args)
        {
            if (sender != args.Source)
                return;
            Worksheet worksheet = GetWorksheet(sender);
            EditComment(sender, worksheet.ActiveCell.Comment);
            args.Handled = true;
        }
        static void OnDeleteComment(object sender, ExecutedRoutedEventArgs args)
        {
            if (sender != args.Source)
                return;
            Worksheet Worksheet = GetWorksheet(sender);
            foreach (Cell c in Worksheet.Selection.GetNonEmptyCells())
                c.Comment = null;
            args.Handled = true;
        }


    }
}
